package com.chat.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chat.admin.Listener.ReadUserExcelDataListener;
import com.chat.admin.domain.DTO.ADD.AddUserDTO;
import com.chat.admin.domain.DTO.DisableAccountDTO;
import com.chat.admin.domain.DTO.EDIT.EditUserDTO;
import com.chat.admin.domain.DTO.UserAppealDTO;
import com.chat.admin.domain.DTO.UserDTO;
import com.chat.admin.domain.DTO.UserFeedbackDTO;
import com.chat.admin.domain.VO.UserAppealVO;
import com.chat.admin.domain.VO.UserFeedbackVO;
import com.chat.admin.domain.VO.UserVO;
import com.chat.admin.domain.pojo.UserExcelEnter;
import com.chat.admin.mapper.UserManagementMapper;
import com.chat.admin.service.IDisableAccountService;
import com.chat.admin.service.IUserAppealService;
import com.chat.admin.service.IUserFeedbackService;
import com.chat.admin.service.UserManagementService;
import com.chat.admin.utils.RedisUtil;
import com.chat.common.common.constant.ExchangeNameConstant;
import com.chat.common.common.constant.MessageConstant;
import com.chat.common.common.constant.RedisKeyConstant;
import com.chat.common.common.constant.RoutingKeyConstant;
import com.chat.common.common.exception.RoutineException;
import com.chat.common.entity.DisableAccount;
import com.chat.common.entity.User;
import com.chat.common.entity.UserAppeal;
import com.chat.common.entity.UserFeedback;
import com.chat.common.form.ListForm;
import com.chat.common.pojo.EmailRequest;
import com.chat.common.vo.R;
import com.chat.openfeign.client.BusinessDataService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserManagementServiceImpl extends ServiceImpl<UserManagementMapper, User> implements UserManagementService {

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private IDisableAccountService idisableAccountService;

    @Autowired
    private IUserAppealService iUserAppealService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private IUserFeedbackService iUserFeedbackService;

//    @Autowired
//    BusinessDataService businessDataService;

    @Override
    public R<ListForm<UserVO>> getUserList(Integer page, Integer size) {
        Page<User> userPage = new Page<>(page, size);
        IPage<User> userList = page(userPage);
        List<UserVO> userListVO = convertToUserVOList(userList);
//        List<UserVO> userListVO = userList.getRecords().stream().map(user -> {
//            UserVO userVO = new UserVO();
//            BeanUtil.copyProperties(user, userVO);
//            String account = (String) redisUtil.get(RedisKeyConstant.USER_ACCOUNT_ForToken + user.getAccount());
//            if(account!=null && !account.isEmpty()){
//                userVO.setOnLine(true);
//            }
//            return userVO;
//        }).collect(Collectors.toList());
        ListForm<UserVO> userListForm = new ListForm<>(page,size,
                userList.getPages(), //总页数
                userList.getTotal(), //总记录数
                userListVO);
        return R.succeed(userListForm);
    }

    @Override
    public R<ListForm<UserVO>> getUserList(UserDTO userDTO) {
        // 从Redis中获取所有在线的用户账号
        Set<String> onlineAccounts = redisUtil.setMembers(RedisKeyConstant.ONLINE_USERS_SET_KEY);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        // 如果账号不为空，则添加账号查询条件
        if (StringUtils.isNotBlank(userDTO.getAccount())) {
            queryWrapper.eq("account", userDTO.getAccount());
        }
        // 如果用户名不为空，则添加用户名查询条件
        if (StringUtils.isNotBlank(userDTO.getUsername())) {
            queryWrapper.eq("username", userDTO.getUsername());
        }
        // 添加状态查询条件
        if(userDTO.getState()!=null){
            queryWrapper.eq("state", userDTO.getState());
        }
        // 根据UserDTO中的onLine字段添加在线状态查询条件
        if (userDTO.getOnLine() != null) {
            if (userDTO.getOnLine()) {
                queryWrapper.in("account", onlineAccounts);
            } else {
                queryWrapper.notIn("account", onlineAccounts);
            }
        }
        Page<User> userPage = new Page<>(userDTO.getPage(), userDTO.getSize());
        IPage<User> userList = page(userPage, queryWrapper);
        List<UserVO> userListVO = convertToUserVOList(userList);
        ListForm<UserVO> userListForm = new ListForm<>(userDTO.getPage(),userDTO.getSize(),
                userList.getPages(), //总页数
                userList.getTotal(), //总记录数
                userListVO);
        return R.succeed(userListForm);
    }

    //TODO:禁用账号应该强制下线
    @Override
    @Transactional
    public R DisableAccount(DisableAccountDTO disableAccountDTO) {
        // 创建一个 UpdateWrapper 对象
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        // 设置 id 等于 userID 的条件
        updateWrapper.eq("id", disableAccountDTO.getUserId());
        // 设置 state 属性为 false
        updateWrapper.set("state", false);
        // 执行更新操作
        boolean result = update(updateWrapper);
        // 根据更新结果返回相应的响应
        if (result) {
            DisableAccount disableAccount = new DisableAccount();
            BeanUtil.copyProperties(disableAccountDTO,disableAccount);
            disableAccount.setCreationTime(LocalDateTime.now());
            //保证原因只有一个。所以先删除以前的
            QueryWrapper<DisableAccount> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id",disableAccountDTO.getUserId());
            idisableAccountService.getBaseMapper().delete(queryWrapper);
            int insert = idisableAccountService.getBaseMapper().insert(disableAccount);
            if (insert == 0) {
                throw new RoutineException("禁用账号失败");
            }
            //强制下线
//            businessDataService.offline(disableAccountDTO.getUserId());
            return R.succeed("账号已成功禁用");
        } else {
            throw new RoutineException("禁用账号失败");
        }
    }

    @Override
    @Transactional
    public R EnableAccount(Long userID) {
        // 创建一个 UpdateWrapper 对象
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        // 设置 id 等于 userID 的条件
        updateWrapper.eq("id", userID);
        // 设置 state 属性为 false
        updateWrapper.set("state", true);
        // 执行更新操作
        boolean result = update(updateWrapper);
        // 根据更新结果返回相应的响应
        if (result) {
            QueryWrapper<DisableAccount> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id",userID);
            idisableAccountService.getBaseMapper().delete(queryWrapper);
            return R.succeed("账号已成功启用");
        } else {
            throw new RoutineException("启用账号失败");
        }
    }

    //TODO: 没加锁，没对数据进行详细判断
    @Override
    public R<UserVO> EditUserInfo(EditUserDTO editUserDTO) {
        // 创建一个 User 对象
        User user = new User();
        // 设置需要更新的属性
        //使用spring自带的映射是防止null对原数据产生影响
        BeanUtils.copyProperties(editUserDTO, user);
        user.setUpdateTime(LocalDateTime.now());
        // 执行更新操作
        boolean result = updateById(user);
        // 根据更新结果返回相应的响应
        if (result) {
            // 如果更新成功，获取更新后的用户信息并返回
            User updatedUser = getById(editUserDTO.getId());
            UserVO updatedUserVO = convertToUserVO(updatedUser);
            return R.succeed("更新用户信息成功",updatedUserVO);
        } else {
            return R.failed("更新用户信息失败");
        }
    }

    @Override
    public R addOneUserAccount(AddUserDTO addUserDTO) {
        User user = null;
        //判断账号是否是由前端提供
        if(addUserDTO.getAccount() == null || addUserDTO.getAccount().isEmpty()) {
            user = new User();
            //转换成User对象
            BeanUtil.copyProperties(addUserDTO,user);
            //获取名为REGISTER_LOCK的Redisson分布式锁
            RLock lock = redissonClient.getLock(RedisKeyConstant.REGISTER_LOCK);
            boolean locked;
            //防止并发生成相同的账号，引入分布式锁
            try {
                // 尝试获取锁，锁的过期时间为10秒
                // TODO 10秒是测试情况具体的情况后续修改
                // 尝试获取锁，最多等待3秒，上锁后10秒自动解锁
                locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
                if(locked) {
                    //生成账号
                    String account = generateAccount();
                    user.setAccount(account);
                    //密码加密
                    user.setPassword(passwordEncoder.encode(addUserDTO.getPassword()));
                    user.setState(1);
                    // 设置时间
                    user.setCreationTime(LocalDateTime.now());
                    user.setUpdateTime(LocalDateTime.now());
                    log.info("无账号 user:{}",user);
                    //baseMapper.insert(user);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                throw new RoutineException(MessageConstant.ADD_ACCOUNT_ERROR);
            } finally {
                // 最后确保分布式锁的释放
                if (lock.isHeldByCurrentThread()) { // 检查当前线程是否保持该锁
                    lock.unlock(); // 如果是，解锁
                }
            }
        } else {
            Boolean accountInRedisExists = redisUtil.hasKey(RedisKeyConstant.REGISTER_USER + addUserDTO.getAccount());
            if(accountInRedisExists){
                //判断账号是否存在redis，存在就抛出错误
                throw new RoutineException(MessageConstant.ACCOUNT_FOUND );
            } else {
                user = baseMapper
                        .selectOne(
                                new LambdaQueryWrapper<User>()
                                        .eq(User::getAccount, addUserDTO.getAccount())
                        );
                if (user != null) {
                    //判断账号是否存在mysql，存在就抛出错误
                    throw new RoutineException(MessageConstant.ACCOUNT_FOUND );
                } else {
                    user = new User();
                    //转换成User对象
                    BeanUtil.copyProperties(addUserDTO,user);
                    user.setPassword(passwordEncoder.encode(addUserDTO.getPassword()));
                    user.setState(1);
                    // 设置时间
                    user.setCreationTime(LocalDateTime.now());
                    user.setUpdateTime(LocalDateTime.now());
                    log.info("有账号:{}",user);
                    baseMapper.insert(user);
                }
            }
        }
        return R.succeed("添加成功");
    }

    @Override
    public R<?> addExcelUserAccount(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), UserExcelEnter.class, new ReadUserExcelDataListener()).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("读取Excel文件时发生错误：{}", e.getMessage());
            return R.failed("读取Excel文件时发生错误");
        }
        return R.succeed("添加成功");
    }

    private UserVO convertToUserVO(User user) {
        UserVO userVO = new UserVO();
        //使用hutool工具包的映射可以转化1和0
        BeanUtil.copyProperties(user, userVO);
        //id太大会超越js的范围，换成string,Long和string不能直接映射需要转化
        userVO.setId(user.getId().toString());
        String account = (String) redisUtil.get(RedisKeyConstant.USER_ACCOUNT_ForToken + user.getAccount());
        if(account!=null && !account.isEmpty()){
            userVO.setOnLine(true);
        }
        return userVO;
    }


//    private List<UserVO> convertToUserVOList(IPage<User> userList) {
//        return userList.getRecords().stream().map(user -> {
//            UserVO userVO = new UserVO();
//            BeanUtil.copyProperties(user, userVO);
//            //id太大会超越js的范围，换成string
//            userVO.setId(user.getId().toString());
//            String account = (String) redisUtil.get(RedisKeyConstant.USER_ACCOUNT_ForToken + user.getAccount());
//            if(account!=null && !account.isEmpty()){
//                userVO.setOnLine(true);
//            }
//            return userVO;
//        }).collect(Collectors.toList());
//    }
    private List<UserVO> convertToUserVOList(IPage<User> userList) {
        return userList.getRecords().stream().map(this::convertToUserVO).collect(Collectors.toList());
    }

    public void convertToUsers(List<UserExcelEnter> userExcels) {
        System.out.println(userExcels);
        List<User> userList = userExcels.stream().map(userExcel -> {
            // 在这里将UserExcel转换为User
            User user = new User();
            BeanUtil.copyProperties(userExcel, user);
            user.setPassword(passwordEncoder.encode(userExcel.getPassword()));
            user.setState(1);
            // 设置时间
            user.setCreationTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            return user;
        }).toList();
        baseMapper.insertBatchSomeColumn(userList);
        //saveBatch(userList);
    }

    @Override
    public R<ListForm<UserAppealVO>> getUserApplication(UserAppealDTO userAppealDTO) {
        return R.succeed(iUserAppealService.getUserApplication(userAppealDTO));
    }

    @Override
    @Transactional
    public R agreeUserApplication(String applyID) {
        if(iUserAppealService.agreeUserAppeal(applyID)){
            UserAppeal userAppeal = iUserAppealService.getUserAppeal(applyID);
            User user = getUserByAccount(userAppeal.getAccount());
            if(userAppeal.getType() == 1){
                EnableAccount(user.getId());
            }
            if(user.getEmail() != null || !user.getEmail().isEmpty()){
                EmailRequest emailRequest = new EmailRequest();
                emailRequest.setToEmail(user.getEmail());
                emailRequest.setSubject("E-chat申诉");
                emailRequest.setBody("对于账号：" + user.getAccount() + "的申诉已通过");
                rabbitTemplate.convertAndSend(ExchangeNameConstant.ECHAT_MAIL_TOPIC_EXCHANGE, RoutingKeyConstant.USERAPPEAL_EMAIL, emailRequest);
                return R.succeed();
            }
            return R.succeed("当前账户未填邮箱，无法通知");
        } else {
            return R.failed("同意申诉失败");
        }
    }

    @Override
    public R refuseUserApplication(String applyID) {
        if(iUserAppealService.refuseUserAppeal(applyID)){
            UserAppeal userAppeal = iUserAppealService.getUserAppeal(applyID);
            User user = getUserByAccount(userAppeal.getAccount());
            if(user.getEmail() != null || !user.getEmail().isEmpty()){
                EmailRequest emailRequest = new EmailRequest();
                emailRequest.setToEmail(user.getEmail());
                emailRequest.setSubject("E-chat申诉");
                emailRequest.setBody("对于账号：" + user.getAccount() + "的申诉未通过");
                rabbitTemplate.convertAndSend(ExchangeNameConstant.ECHAT_MAIL_TOPIC_EXCHANGE, RoutingKeyConstant.USERAPPEAL_EMAIL, emailRequest);
                return R.succeed();
            }
            return R.succeed("当前账户未填邮箱，无法通知");
        } else {
            return R.failed("拒绝申诉失败");
        }
    }

    @Override
    public User getUserByAccount(String Account) {
        User user = baseMapper
                .selectOne(
                        new LambdaQueryWrapper<User>()
                                .eq(User::getAccount, Account)
                );
        if (user==null){
            //判断账号是否存在
            throw new RoutineException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        return user;
    }

    @Override
    public R<ListForm<UserFeedbackVO>> getUserFeedback(UserFeedbackDTO userFeedbackDTO) {
        return R.succeed(iUserFeedbackService.getUserFeedbacks(userFeedbackDTO));
    }

    @Override
    public R HandleUserFeedback(String ID) {
        Integer integer = iUserFeedbackService.HandleUserFeedback(ID);
        if(integer < 1){
            return R.failed("处理失败");
        } else {
            return R.succeed("处理成功");
        }
    }

    /**
     * 生成账号
     * 未测试
     * @return
     */
    public String generateAccount() {
        String account;
        User user = null;
        Boolean accountInRedisExists;
        do {
            // 首先随机生成账号的长度，长度范围在6-10位
            int length = RandomUtil.randomInt(6, 11);
            // 确保首位数字为1-9之间
            int firstDigit = RandomUtil.randomInt(1, 10);
            // 其他位置的数字可以为0-9之间
            String otherDigits = RandomUtil.randomNumbers(length - 1);
            // 拼接首位数字和其他数字
            account = firstDigit + otherDigits;
            // 从Redis查询账户
            accountInRedisExists = redisUtil.hasKey(RedisKeyConstant.REGISTER_USER + account);
            if (!accountInRedisExists){
                //不存在再去数据库中查
                user = baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getAccount, account));
            }
        } while (accountInRedisExists || user != null); // 如果用户存在，则重新生成账号

        return account;
    }

}
